package com.yq.ylmall.acl.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yq.ylmall.acl.mapper.UserMapper;
import com.yq.ylmall.acl.mapper.UserRoleMapper;
import com.yq.ylmall.acl.service.RoleService;
import com.yq.ylmall.acl.service.UserService;
import com.yq.ylmall.common.utils.AuthContextUtil;
import com.yq.ylmall.common.utils.JwtUtils;
import com.yq.ylmall.dto.acl.AssignRoleDto;
import com.yq.ylmall.dto.acl.LoginDto;
import com.yq.ylmall.dto.acl.UserQueryDto;
import com.yq.ylmall.exception.ServiceException;
import com.yq.ylmall.model.acl.User;
import com.yq.ylmall.model.acl.UserRole;
import com.yq.ylmall.result.ResultCode;
import com.yq.ylmall.vo.acl.LoginVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserRoleMapper userRoleMapper;
    @Autowired
    private RedisTemplate<String , String> redisTemplate;



    @Override
    public LoginVo login(LoginDto loginDto) {

        // 校验验证码是否正确
        String captcha = loginDto.getCaptcha();     // 用户输入的验证码
        String codeKey = loginDto.getCodeKey();     // redis中验证码的数据key

        // 从Redis中获取验证码
        String redisCode = redisTemplate.opsForValue().get("user:login:validatecode:" + codeKey);
        if(StrUtil.isEmpty(redisCode) || !StrUtil.equalsIgnoreCase(redisCode , captcha)) {
            throw new ServiceException(ResultCode.VALIDATECODE_ERROR) ;
        }

        // 验证通过删除redis中的验证码
        redisTemplate.delete("user:login:validatecode:" + codeKey) ;

        // 根据用户名查询用户
        User user = userMapper.selectByUserName(loginDto.getUsername());
        if(user == null) {
            throw new ServiceException(ResultCode.INCORRECT_USERNAME_OR_PASSWORD);
        }

        //检测用户的密码是否匹配
        //1.先获取数据库中的加密之后的密码和盐值
        String dbPassword = user.getPassword();
        String dbSalt = user.getSalt();
        //2.与用户输入的密码进行比较
        String inputPassword = loginDto.getPassword();
        //2.1 根据用户输入的密码按照相同的md5算法的规则进行加密
        String md5InputPassword = getMD5Password(inputPassword, dbSalt);
        //3 判断加密后密码与数据库加密后的密码是否一致
        if (!md5InputPassword.equals(dbPassword)){
            throw new ServiceException(ResultCode.INCORRECT_USERNAME_OR_PASSWORD);
        }

        // 生成令牌，保存数据到Redis中


        String token = JwtUtils.createAdminToken(user.getId());
        // TODO redis测试 token 设置为 7天
        redisTemplate.opsForValue().set("user:login:" + token , JSON.toJSONString(user) , 7 , TimeUnit.DAYS);
        // 构建响应结果对象
        LoginVo loginVo = new LoginVo() ;
        loginVo.setToken(token);
        loginVo.setRefresh_token("");

        // 返回
        return loginVo;
    }


    @Override
    public IPage<User> selectPage(Page<User> pageParam, UserQueryDto userQueryVo) {
        //获取关键字称条件值
        String keyword = userQueryVo.getKeyword();
        //创建条件构造器
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .like(!StringUtils.isEmpty(keyword), User::getUsername, keyword)
                .or()
                .like(!StringUtils.isEmpty(keyword), User::getName, keyword);
        //调用mapper方法
        IPage<User> pageModel = baseMapper.selectPage(pageParam,wrapper);
        return pageModel;
    }

    /**
     * 注册用户
     * @param user
     */
    @Override
    public void saveUser(User user) {
        //获取用户名
        String username = user.getUsername();
        //判断是否被注册过
        User selectedByUserName = baseMapper.selectByUserName(username);
        if (selectedByUserName != null){
            throw new ServiceException(ResultCode.THE_USERNAME_HAS_BEEN_REGISTERED);
        }
        //获取输入的密码
        String inputPassword = user.getPassword();
        String salt = UUID.randomUUID().toString().toUpperCase();
        String md5Password = getMD5Password(inputPassword, salt);
        user.setSalt(salt);
        user.setPassword(md5Password);
        //注册用户
        int rows = baseMapper.insert(user);
        if (rows != 1){
            throw new ServiceException(ResultCode.USER_REGISTRATION_FAILED);
        }
    }

    /**
     * 修改用户
     * @param user
     */
    @Override
    public void updateUserById(User user) {
        //获取输入的密码
        String inputPassword = user.getPassword();
        String salt = UUID.randomUUID().toString().toUpperCase();
        String md5Password = getMD5Password(inputPassword, salt);
        user.setSalt(salt);
        user.setPassword(md5Password);
        int rows = baseMapper.updateById(user);
        if (rows != 1){
            throw new ServiceException(ResultCode.USER_MODIFICATION_FAILED);
        }
    }

    @Override
    public User getUserInfo(String token) {
        String userJson = redisTemplate.opsForValue().get("user:login:" + token);
        return JSON.parseObject(userJson , User.class);
    }

    @Override
    public void logout(String token) {
        redisTemplate.delete("user:login:" + token) ;
    }

    @Transactional
    @Override
    public void doAssign(AssignRoleDto assignRoleDto) {
        // 删除之前的所有的用户所对应的角色数据
        userRoleMapper.deleteByUserId(assignRoleDto.getUserId());
        // 分配新的角色数据
        List<Long> roleIdList = assignRoleDto.getRoleIdList();
        UserRole userRole = new UserRole();
        roleIdList.forEach(roleId->{
            userRole.setUserId(assignRoleDto.getUserId());
            userRole.setRoleId(roleId);
            userRoleMapper.insert(userRole);
        });
    }

    /**
     * 定义一个md5算法的加密处理
     */
    private String getMD5Password(String password,String salt){
        for (int i = 0; i < 3; i++) {
            //md5加密算法方法的调用(进行三次加密)
            password = DigestUtils.md5DigestAsHex((salt+password+salt).getBytes()).toUpperCase();
        }
        //返回加密之后的密码
        return password;
    }
}
